#include <assert.h>
#include <map>
#include <vector>
#include <time.h>
#include <GL/glut.h>
#include "SceneManager.hpp"
#include "Entities/BaseEntity.hpp"
#include "ScreenSystem/ScreenManager.hpp"
#include "ScreenSystem/DemoSceneScreen.hpp"
#include "HDRDemoConstants.hpp"

//! Setting values for static variables.
SceneManager*            SceneManager::m_instance = 0;


//! GetInstance method returns pointer to SceneManager instance
//! and ensures that only one such instance exists.
SceneManager* SceneManager::GetInstance()
{
	if( !m_instance )
	{
		m_instance = new SceneManager;
	}

	return m_instance;
}

SceneManager::SceneManager()
{
	m_startTick = clock();
	m_idIndex   = 0;
}

//! Method registering entity with its unique ID number.
void SceneManager::RegisterEntity( BaseEntity* newEntity )
{
	// Check if entity exists.
	assert( newEntity );

	// Register entity with unique ID.
	newEntity->SetEntityID( GenerateUniqueID() );
	//m_entitiesMap.insert( std::make_pair( newEntity->GetEntityID(), newEntity ) );

	// Mateusz zaproponowal prostsze konstrukcyjnie rozwiazanie
	m_entitiesMap[newEntity->GetEntityID()] = newEntity;
}

//! Here all entities are unregistered and destroyed.
void SceneManager::UnregisterAllEntities()
{
	map<int, BaseEntity*>::iterator mapBegin = m_entitiesMap.begin();
	map<int, BaseEntity*>::iterator mapEnd   = m_entitiesMap.end();

	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
	{
		m_entitiesToDestroy.push_back( mapBegin->first );
	}



	vector<int>::iterator beginOfUnregistered = m_entitiesToDestroy.begin();
	vector<int>::iterator endOfUnregistered   = m_entitiesToDestroy.end();

	// Destroy unregistered entities.
	for( beginOfUnregistered; beginOfUnregistered != endOfUnregistered; ++ beginOfUnregistered )
	{
		delete m_entitiesMap[ *beginOfUnregistered ];
		m_entitiesMap[ *beginOfUnregistered ] = NULL;
		//m_entitiesMap.erase( *beginOfUnregistered );
	}
}

//! Unregistered entity will be destroyed on next UpdateScene() call.
void SceneManager::UnregisterEntity(int entityID)
{
	m_entitiesToDestroy.push_back( entityID );
}

//! Method updating whole scene. First step is to destroy unregistered entities.
//! Second is to update state of registered entities. Third step is to render all
//! registered entities.
void SceneManager::UpdateScene( clock_t tick )
{
	map<int, BaseEntity*>::iterator mapBegin = GetInstance()->m_entitiesMap.begin();
	map<int, BaseEntity*>::iterator mapEnd   = GetInstance()->m_entitiesMap.end();

	vector<int>::iterator beginOfUnregistered = GetInstance()->m_entitiesToDestroy.begin();
	vector<int>::iterator endOfUnregistered   = GetInstance()->m_entitiesToDestroy.end();

	// Destroy unregistered entities.
	for( beginOfUnregistered; beginOfUnregistered != endOfUnregistered; ++ beginOfUnregistered )
	{
		delete GetInstance()->m_entitiesMap[ *beginOfUnregistered ];
		GetInstance()->m_entitiesMap[ *beginOfUnregistered ] = NULL;

		//m_entitiesMap.erase( *beginOfUnregistered );
	}

	GetInstance()->m_entitiesToDestroy.clear();


	// Update all registered entities.
	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
		mapBegin->second->Update( clock() );



/////////////////////////////////////////////////////////
//! RENDERING PART GOES HERE ////////////////////////////
/////////////////////////////////////////////////////////
	mapBegin = GetInstance()->m_entitiesMap.begin();
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	//ExtensionsLoader::glUseProgramObjectARB( 0 );
	
	((ShadowMapFBO*)(ScreenManager::GetInstance()->GetCurrentScreen()))->RenderToFBO( true );

	// Render all registered entities.
	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
		mapBegin->second->Render();

	((ShadowMapFBO*)(ScreenManager::GetInstance()->GetCurrentScreen()))->RenderToFBO( false );

	glDisable( GL_TEXTURE_2D );
	mapBegin = GetInstance()->m_entitiesMap.begin();
	// Render all registered entities.
	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
		mapBegin->second->Render();


	glEnable( GL_TEXTURE_2D );
//	glDisable( GL_LIGHTING );
	glBindTexture(GL_TEXTURE_2D, ((ShadowMapFBO*)(ScreenManager::GetInstance()->GetCurrentScreen()))->GetShadowMapID() );

//	//ExtensionsLoader::glUseProgramObjectARB( ((DemoSceneScreen*)(ScreenManager::GetInstance()->GetCurrentScreen()))->m_shader );
//
	//GLfloat MaterialDiffuse[4],
	//	    MaterialSpecular[4];

	//float   MaterialShininess[1];

	//MaterialDiffuse[0] = 0.1;
	//MaterialDiffuse[1] = 0.75;
	//MaterialDiffuse[2] = 0.1;
	//MaterialDiffuse[3] = 1.0f;
	//MaterialSpecular[0] = 1.0f;
	//MaterialSpecular[1] = 1.0f;
	//MaterialSpecular[2] = 1.0f;
	//MaterialSpecular[3] = 1.0f;
	//MaterialShininess[0] = 8.0f;
	//glMaterialfv( GL_FRONT, GL_DIFFUSE, MaterialDiffuse );
	//glMaterialfv( GL_FRONT, GL_SPECULAR, MaterialSpecular );
	//glMaterialfv( GL_FRONT, GL_SHININESS, MaterialShininess );

	glBegin( GL_QUADS );
		glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -10.0f, -5.0f, -15.0f );
		glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -10.0f, -5.0f, 10.0f );
		glTexCoord2f( 0.0f, 0.0f ); glVertex3f( 15.0f, -5.0f, 10.0f );
		glTexCoord2f( 0.0f, 1.0f ); glVertex3f( 15.0f, -5.0f, -15.0f );
	glEnd();

	glutSwapBuffers();
}